/*
 * RestoreScript.java
 *
 * Created on November 3, 2007, 9:59 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package filecopier.script;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import custom.xml.DOMHelperException;

/**
 *
 * @author david
 */
public class RestoreScript extends AbstractScript {

    private static final String namespace = "http://sourceforge.net/projects/filecopier01/restore";

    /** Creates a new instance of RestoreScript */
    public RestoreScript() throws DOMHelperException {
        super(RestoreScript.class.getResource("RestoreScript.xsd"), filecopier.Singleton.getInstance().getOptions().getRestorePathWrapper().getRestorePath());
    }

    public RestoreScript(final File scriptFile) throws DOMHelperException, IOException {
        super(RestoreScript.class.getResource("RestoreScript.xsd"), filecopier.Singleton.getInstance().getOptions().getRestorePathWrapper().getRestorePath(), scriptFile);
    }

    public boolean addScriptEntry(final ScriptEntry scriptEntry) {
        if (scriptEntry == null) {
            throw new IllegalArgumentException("addScriptEntry() was passed a null for the ScriptEntry.");
        }
        if (!(scriptEntry instanceof RestoreScriptEntry)) {
            throw new IllegalArgumentException("addScriptEntry() was passed a ScriptEntry object that was not " +
                    "of type RestoreScriptEntry.");
        }
        scriptEntry.setSavedState(ScriptEntry.SavedState.Added);
        boolean add = true;
        for (ScriptEntry se : scriptEntries) {
            if (se.equals(scriptEntry)) {
                add = false;
                break;
            }
        }
        if (add) {
            scriptEntries.add(scriptEntry);
        }
        return add;
    }

    public boolean removeScriptEntry(final ScriptEntry scriptEntry) {
        boolean removed = false;
        if (scriptEntry == null) {
            throw new IllegalArgumentException("removeScriptEntry() was passed a null for the ScriptEntry object.");
        }
        if (!(scriptEntry instanceof RestoreScriptEntry)) {
            throw new IllegalArgumentException("removeScriptEntry() was passed a ScriptEntry object " +
                    "that was not of type RestoreScriptEntry.");
        }
        ScriptEntry scriptToRemove = null;
        // See if this ScriptEntry object is in this RestoreScript object.
        for (ScriptEntry se : scriptEntries) {
            boolean equal = se.equals(scriptEntry);
            if (equal) {
                scriptToRemove = se;
                break;
            }
        }
        // If the ScriptEntry was found, flag it for removal.
        if (scriptToRemove != null) {
            // If the state of this entry is saved, flag it for removal.
            if (scriptToRemove.getSavedState() == ScriptEntry.SavedState.Saved) {
                scriptToRemove.setSavedState(ScriptEntry.SavedState.Removed);
            } // If the state of this entry is added, it has not been added to the file, so remove it from the list.
            else {
                scriptEntries.remove(scriptToRemove);
            }
            removed = true;
        }

        return removed;
    }

    public boolean insertScriptEntry(final ScriptEntry scriptEntry, int idx) {
        if (scriptEntry == null) {
            throw new IllegalArgumentException("insertScriptEntry() was passed a null for the ScriptEntry.");
        }
        if (!(scriptEntry instanceof RestoreScriptEntry)) {
            throw new IllegalArgumentException("addScriptEntry() was passed a ScriptEntryInterface object that was not " +
                    "of type RestoreScriptEntry.");
        }
        scriptEntry.setSavedState(ScriptEntry.SavedState.Added);
        scriptEntries.add(idx, scriptEntry);
        return true;
    }

    public List<ScriptEntry> getScriptEntries(final ScriptEntry.PathType pathType) {
        throw new UnsupportedOperationException("getScriptEntries(final ScriptEntry.PathType pathType) " +
                "is not allowed for a RestoreScript object.");
    }

    public List<ScriptEntry> getScriptEntries(final ScriptEntry.SavedState savedState) {
        if (savedState == null) {
            throw new IllegalArgumentException("initScriptEntries() was passed a null for the SavedState.");
        }

        List<ScriptEntry> typeEntries = new ArrayList<ScriptEntry>();
        for (ScriptEntry se : scriptEntries) {
            if (se.getSavedState() == savedState) {
                typeEntries.add(se);
            }
        }

        return typeEntries;
    }

    public boolean isTemp() {
        // A restore script does not have a temp value.
        return false;
    }

    public void setTemp(boolean temp) {
    // A restore script does not have a temp value.
    }

    protected List<ScriptEntry> initScriptEntries() {
        if (scriptEntries == null) {
            scriptEntries = new ArrayList<ScriptEntry>();
        }
        Document document = domUtilities.getDocument();
        if (document.getDocumentElement() != null) {
            NodeList nodeList = document.getFirstChild().getChildNodes();
            int length = nodeList.getLength();

            for (int i = 0; i < length; i++) {
                Node node = nodeList.item(i);
                if (node.getNodeName().equals("Pair")) {
                    String restorePathStr = null;
                    String deletePathStr = null;

                    NodeList pairList = node.getChildNodes();
                    int pairLength = pairList.getLength();
                    for (int j = 0; j < pairLength; j++) {
                        node = pairList.item(j);
                        if (node.getNodeName().equals("RestorePath")) {
                            restorePathStr = node.getFirstChild().getNodeValue();
                        } else if (node.getNodeName().equals("DeletePath")) {
                            deletePathStr = node.getFirstChild().getNodeValue();
                        }
                    }

                    ScriptEntry scriptEntry = new RestoreScriptEntry(ScriptEntry.SavedState.Saved,
                            new File(restorePathStr), new File(deletePathStr));
                    scriptEntries.add(scriptEntry);
                }
            }
        }
        return scriptEntries;
    }

    protected Document createDocument() throws IOException {
        scriptFile.createNewFile();
        Document doc = domUtilities.getDocument();
        Element restoreScript = doc.createElementNS(namespace, "RestoreScript");
        restoreScript.setAttribute("xmlns", namespace);
        doc.appendChild(restoreScript);
        return doc;
    }

    protected Document createDocument(boolean tempScript) throws IOException {
        return (createDocument());
    }

    protected Element createElement(final ScriptEntry scriptEntry) {
        Document document = domUtilities.getDocument();

        Element pair = document.createElement("Pair");
        pair.appendChild(domUtilities.buildSimpleElement("RestorePath",
                scriptEntry.getPath(ScriptEntry.PathType.Restore).toString()));
        pair.appendChild(domUtilities.buildSimpleElement("DeletePath",
                scriptEntry.getPath(ScriptEntry.PathType.Delete).toString()));

        Node restoreScript = document.getFirstChild();
        restoreScript.appendChild(pair);
        return pair;
    }

    protected Element insertElement(final ScriptEntry scriptEntry, int idx) {
        Element pair = null;
        Document document = domUtilities.getDocument();
        Element restoreScript = document.getDocumentElement();
        org.w3c.dom.NodeList nodeList = restoreScript.getElementsByTagName("Pair");
        int length = nodeList.getLength();
        if (idx < length - 1) {
            pair = document.createElement("Pair");
            pair.appendChild(domUtilities.buildSimpleElement("RestorePath",
                    scriptEntry.getPath(ScriptEntry.PathType.Restore).toString()));
            pair.appendChild(domUtilities.buildSimpleElement("DeletePath",
                    scriptEntry.getPath(ScriptEntry.PathType.Delete).toString()));

            org.w3c.dom.Node node = nodeList.item(idx);
            restoreScript.insertBefore(pair, node);
        } else {
            pair = createElement(scriptEntry);
        }

        return pair;
    }

    protected boolean removeElement(final ScriptEntry se) {
        boolean removed = false;
        Element restoreScript = domUtilities.getDocument().getDocumentElement();
        org.w3c.dom.NodeList nodeList = restoreScript.getElementsByTagName("Pair");
        for (int i = 0; removed == false && i < nodeList.getLength(); i++) {
            org.w3c.dom.Node pair = nodeList.item(i);
            org.w3c.dom.NodeList nl = pair.getChildNodes();
            for (int j = 0; j < nl.getLength(); j++) {
                org.w3c.dom.Node n = nl.item(j);
                if (n != null && n.getNextSibling() != null) {
                    Node restore = n.getNextSibling().getFirstChild();
                    if (restore != null) {
                        Node next = n.getNextSibling().getNextSibling();
                        if (next != null && next.getNextSibling() != null) {
                            Node delete = next.getNextSibling().getFirstChild();
                            if (delete != null) {
                                ScriptEntry toRemove = new RestoreScriptEntry(new File(restore.getNodeValue()),
                                        new File(delete.getNodeValue()));
                                if (toRemove.equals(se)) {
                                    restoreScript.removeChild(pair);
                                    removed = true;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return removed;
    }

    @Override
    protected boolean convertOldScript() throws DOMHelperException {
        boolean converted = false;
        Properties properties = new Properties();
        try {
            File scriptFile = getScriptFile();
            InputStream is = new FileInputStream(scriptFile);
            properties.load(is);
            is.close();
            if (isOldScript(properties)) {
                // Put the previous version of FileCopier (1.04) between the file name and its extension. This file
                // can be used if the user decides he prefers the old version of FileCopier.
                String oldScriptName = buildOldScriptName(scriptFile.getPath());
                File oldScriptFile = new File(oldScriptName);
                File tempScriptFile = scriptFile;
                tempScriptFile.renameTo(oldScriptFile);

                // We just renamed the file, so the file with the original name does not exist anymore. Call init.
                // This time, it should not get a DOMHelperException
                init();
                int addedCt = 0;

                for (int i = 1;; i++) {
                    String restoreStr = properties.getProperty("file" + i + ".restore");
                    if (restoreStr != null) {
                        String deleteStr = properties.getProperty("file" + i + ".delete");
                        ScriptEntry se = new RestoreScriptEntry(new File(restoreStr), new File(deleteStr));
                        addScriptEntry(se);
                        addedCt++;
                    } else {
                        break;
                    }
                }

                if (addedCt > 0) {
                    // At least one entry was added to the script file, so go ahead and save it.
                    // First, set the class instance of scriptFile to null, so that this will be treated as the creation of a new file.
                    this.scriptFile = null;
                    saveToDisk(scriptFile);
                    // Make sure what we just created is a valid script file.
                    try {
                        validate();
                    } catch (DOMHelperException e) {
                        // The file is not valid, so preserve the old script file.
                        oldScriptFile.renameTo(scriptFile);
                        throw e;
                    }
                    converted = true;
                    filecopier.Singleton.getInstance().getFileConsoleLogger().info("\"" + scriptFile + "\" was in the old format." +
                            " It was successfully converted to the new format.");
                } else {
                    // Rename the old temp file to its original name.
                    oldScriptFile.renameTo(scriptFile);
                    filecopier.Singleton.getInstance().getFileConsoleLogger().warn("\"" + scriptFile + "\" was in the old format." +
                            " It could not be converted to the new format.");
                }
            }
        } catch (IOException e) {
        // If the file is not found, then it is definitely not a script file with the old format. Return false.
        }

        return converted;
    }

    public String buildOldScriptName(final String scriptName) {
        String nameOnly, ext, sep;

        int idx = scriptName.lastIndexOf('.');
        if (idx != -1) {
            // File has an extension, so put the name and extension in separate Strings.
            nameOnly = scriptName.substring(0, idx);
            ext = scriptName.substring(idx);
            // Make the separator a dot.
            sep = ".";
        } else {
            /* File has no extension, so set sNameOnly to point to the whole name and set sExt to point to
            an empty String. */
            nameOnly = scriptName;
            ext = "";
            // Make the separator an underscore, so the sytem won't think the added date is an extension.
            sep = "_";
        }

        return (nameOnly + sep + "1" + sep + "04" + ext);
    }

    private boolean isOldScript(Properties properties) {
        boolean isOld = false;
        int i = 1;

        while (properties.getProperty("file" + i + ".restore") != null) {
            isOld = properties.getProperty("file" + (i++) + ".delete") != null;
        }
        return isOld;
    }

    @Override
    protected File getFullScriptPath(File scriptFile) {
        if (!scriptFile.getPath().contains(System.getProperty("file.separator"))) {
            String fullScriptPath = filecopier.Singleton.getInstance().getOptions().getRestorePathWrapper().getRestorePath().getFilePath(scriptFile.getPath());
            if (fullScriptPath != null) {
                scriptFile = new File(fullScriptPath);
            }
        }
        return scriptFile;
    }
}
